package meter

import (
	"fmt"
)

// ErrorCode represents different types of meter API errors.
type ErrorCode int

const (
	// ErrUnknown represents an unknown error.
	ErrUnknown ErrorCode = iota
	// ErrInvalidConfig represents configuration validation errors.
	ErrInvalidConfig
	// ErrConnectionFailed represents network connection errors.
	ErrConnectionFailed
	// ErrAPIError represents API-specific errors.
	ErrAPIError
	// ErrInvalidRequest represents invalid request errors.
	ErrInvalidRequest
	// ErrResponseParsing represents response parsing errors.
	ErrResponseParsing
	// ErrAuthentication represents authentication errors.
	ErrAuthentication
	// ErrRateLimited represents rate limiting errors.
	ErrRateLimited
	// ErrNotFound represents resource not found errors.
	ErrNotFound
	// ErrTimeout represents request timeout errors.
	ErrTimeout
)

// String returns the string representation of the error code.
func (e ErrorCode) String() string {
	switch e {
	case ErrUnknown:
		return "UNKNOWN"
	case ErrInvalidConfig:
		return "INVALID_CONFIG"
	case ErrConnectionFailed:
		return "CONNECTION_FAILED"
	case ErrAPIError:
		return "API_ERROR"
	case ErrInvalidRequest:
		return "INVALID_REQUEST"
	case ErrResponseParsing:
		return "RESPONSE_PARSING"
	case ErrAuthentication:
		return "AUTHENTICATION"
	case ErrRateLimited:
		return "RATE_LIMITED"
	case ErrNotFound:
		return "NOT_FOUND"
	case ErrTimeout:
		return "TIMEOUT"
	default:
		return "UNKNOWN"
	}
}

// MeterError represents a meter API specific error.
type MeterError struct {
	Code    ErrorCode `json:"code"`
	Message string    `json:"message"`
	Details string    `json:"details,omitempty"`
}

// Error implements the error interface.
func (e *MeterError) Error() string {
	if e.Details != "" {
		return fmt.Sprintf("[%s] %s: %s", e.Code.String(), e.Message, e.Details)
	}
	return fmt.Sprintf("[%s] %s", e.Code.String(), e.Message)
}

// NewMeterError creates a new MeterError with the specified code and message.
func NewMeterError(code ErrorCode, message string) *MeterError {
	return &MeterError{
		Code:    code,
		Message: message,
	}
}

// NewMeterErrorWithDetails creates a new MeterError with code, message, and details.
func NewMeterErrorWithDetails(code ErrorCode, message, details string) *MeterError {
	return &MeterError{
		Code:    code,
		Message: message,
		Details: details,
	}
}

// IsErrorCode checks if the error is a MeterError with the specified code.
func IsErrorCode(err error, code ErrorCode) bool {
	if meterErr, ok := err.(*MeterError); ok {
		return meterErr.Code == code
	}
	return false
}

// GetErrorCode extracts the error code from a MeterError, returns ErrUnknown for other errors.
func GetErrorCode(err error) ErrorCode {
	if meterErr, ok := err.(*MeterError); ok {
		return meterErr.Code
	}
	return ErrUnknown
}

// WrapError wraps an existing error with a MeterError.
func WrapError(code ErrorCode, message string, err error) *MeterError {
	if err == nil {
		return NewMeterError(code, message)
	}
	return NewMeterErrorWithDetails(code, message, err.Error())
}
